# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from typing import (
    Callable,
    Final,
    Generator,
    Iterable,
    Optional,
    Sequence,
    Union,
)

from opentelemetry.metrics import (
    CallbackOptions,
    Counter,
    Meter,
    ObservableGauge,
    Observation,
    UpDownCounter,
)

# pylint: disable=invalid-name
CallbackT = Union[
    Callable[[CallbackOptions], Iterable[Observation]],
    Generator[Iterable[Observation], CallbackOptions, None],
]

CONTAINER_CPU_TIME: Final = "container.cpu.time"
"""
Total CPU time consumed
Instrument: counter
Unit: s
Note: Total CPU time consumed by the specific container on all available CPU cores.
"""


def create_container_cpu_time(meter: Meter) -> Counter:
    """Total CPU time consumed"""
    return meter.create_counter(
        name=CONTAINER_CPU_TIME,
        description="Total CPU time consumed.",
        unit="s",
    )


CONTAINER_CPU_USAGE: Final = "container.cpu.usage"
"""
Container's CPU usage, measured in cpus. Range from 0 to the number of allocatable CPUs
Instrument: gauge
Unit: {cpu}
Note: CPU usage of the specific container on all available CPU cores, averaged over the sample window.
"""


def create_container_cpu_usage(
    meter: Meter, callbacks: Optional[Sequence[CallbackT]]
) -> ObservableGauge:
    """Container's CPU usage, measured in cpus. Range from 0 to the number of allocatable CPUs"""
    return meter.create_observable_gauge(
        name=CONTAINER_CPU_USAGE,
        callbacks=callbacks,
        description="Container's CPU usage, measured in cpus. Range from 0 to the number of allocatable CPUs.",
        unit="{cpu}",
    )


CONTAINER_DISK_IO: Final = "container.disk.io"
"""
Disk bytes for the container
Instrument: counter
Unit: By
Note: The total number of bytes read/written successfully (aggregated from all disks).
"""


def create_container_disk_io(meter: Meter) -> Counter:
    """Disk bytes for the container"""
    return meter.create_counter(
        name=CONTAINER_DISK_IO,
        description="Disk bytes for the container.",
        unit="By",
    )


CONTAINER_FILESYSTEM_AVAILABLE: Final = "container.filesystem.available"
"""
Container filesystem available bytes
Instrument: updowncounter
Unit: By
Note: In K8s, this metric is derived from the
[FsStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field
of the [ContainerStats.Rootfs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#ContainerStats)
of the Kubelet's stats API.
"""


def create_container_filesystem_available(meter: Meter) -> UpDownCounter:
    """Container filesystem available bytes"""
    return meter.create_up_down_counter(
        name=CONTAINER_FILESYSTEM_AVAILABLE,
        description="Container filesystem available bytes.",
        unit="By",
    )


CONTAINER_FILESYSTEM_CAPACITY: Final = "container.filesystem.capacity"
"""
Container filesystem capacity
Instrument: updowncounter
Unit: By
Note: In K8s, this metric is derived from the
[FsStats.CapacityBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field
of the [ContainerStats.Rootfs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#ContainerStats)
of the Kubelet's stats API.
"""


def create_container_filesystem_capacity(meter: Meter) -> UpDownCounter:
    """Container filesystem capacity"""
    return meter.create_up_down_counter(
        name=CONTAINER_FILESYSTEM_CAPACITY,
        description="Container filesystem capacity.",
        unit="By",
    )


CONTAINER_FILESYSTEM_USAGE: Final = "container.filesystem.usage"
"""
Container filesystem usage
Instrument: updowncounter
Unit: By
Note: This may not equal capacity - available.

In K8s, this metric is derived from the
[FsStats.UsedBytes](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#FsStats) field
of the [ContainerStats.Rootfs](https://pkg.go.dev/k8s.io/kubelet@v0.33.0/pkg/apis/stats/v1alpha1#ContainerStats)
of the Kubelet's stats API.
"""


def create_container_filesystem_usage(meter: Meter) -> UpDownCounter:
    """Container filesystem usage"""
    return meter.create_up_down_counter(
        name=CONTAINER_FILESYSTEM_USAGE,
        description="Container filesystem usage.",
        unit="By",
    )


CONTAINER_MEMORY_AVAILABLE: Final = "container.memory.available"
"""
Container memory available
Instrument: updowncounter
Unit: By
Note: Available memory for use.  This is defined as the memory limit - workingSetBytes. If memory limit is undefined, the available bytes is omitted.
In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and by subtracting the `container_memory_working_set_bytes` metric from the `container_spec_memory_limit_bytes` metric.
In K8s, this metric is derived from the [MemoryStats.AvailableBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API.
"""


def create_container_memory_available(meter: Meter) -> UpDownCounter:
    """Container memory available"""
    return meter.create_up_down_counter(
        name=CONTAINER_MEMORY_AVAILABLE,
        description="Container memory available.",
        unit="By",
    )


CONTAINER_MEMORY_PAGING_FAULTS: Final = "container.memory.paging.faults"
"""
Container memory paging faults
Instrument: counter
Unit: {fault}
Note: In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_failures_total{failure_type=pgfault, scope=container}` and `container_memory_failures_total{failure_type=pgmajfault, scope=container}`metric.
In K8s, this metric is derived from the [MemoryStats.PageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) and [MemoryStats.MajorPageFaults](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API.
"""


def create_container_memory_paging_faults(meter: Meter) -> Counter:
    """Container memory paging faults"""
    return meter.create_counter(
        name=CONTAINER_MEMORY_PAGING_FAULTS,
        description="Container memory paging faults.",
        unit="{fault}",
    )


CONTAINER_MEMORY_RSS: Final = "container.memory.rss"
"""
Container memory RSS
Instrument: updowncounter
Unit: By
Note: In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_rss` metric.
In K8s, this metric is derived from the [MemoryStats.RSSBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API.
"""


def create_container_memory_rss(meter: Meter) -> UpDownCounter:
    """Container memory RSS"""
    return meter.create_up_down_counter(
        name=CONTAINER_MEMORY_RSS,
        description="Container memory RSS.",
        unit="By",
    )


CONTAINER_MEMORY_USAGE: Final = "container.memory.usage"
"""
Memory usage of the container
Instrument: counter
Unit: By
Note: Memory usage of the container.
"""


def create_container_memory_usage(meter: Meter) -> Counter:
    """Memory usage of the container"""
    return meter.create_counter(
        name=CONTAINER_MEMORY_USAGE,
        description="Memory usage of the container.",
        unit="By",
    )


CONTAINER_MEMORY_WORKING_SET: Final = "container.memory.working_set"
"""
Container memory working set
Instrument: updowncounter
Unit: By
Note: In general, this metric can be derived from [cadvisor](https://github.com/google/cadvisor/blob/v0.53.0/docs/storage/prometheus.md#prometheus-container-metrics) and specifically the `container_memory_working_set_bytes` metric.
In K8s, this metric is derived from the [MemoryStats.WorkingSetBytes](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#MemoryStats) field of the [PodStats.Memory](https://pkg.go.dev/k8s.io/kubelet@v0.34.0/pkg/apis/stats/v1alpha1#PodStats) of the Kubelet's stats API.
"""


def create_container_memory_working_set(meter: Meter) -> UpDownCounter:
    """Container memory working set"""
    return meter.create_up_down_counter(
        name=CONTAINER_MEMORY_WORKING_SET,
        description="Container memory working set.",
        unit="By",
    )


CONTAINER_NETWORK_IO: Final = "container.network.io"
"""
Network bytes for the container
Instrument: counter
Unit: By
Note: The number of bytes sent/received on all network interfaces by the container.
"""


def create_container_network_io(meter: Meter) -> Counter:
    """Network bytes for the container"""
    return meter.create_counter(
        name=CONTAINER_NETWORK_IO,
        description="Network bytes for the container.",
        unit="By",
    )


CONTAINER_UPTIME: Final = "container.uptime"
"""
The time the container has been running
Instrument: gauge
Unit: s
Note: Instrumentations SHOULD use a gauge with type `double` and measure uptime in seconds as a floating point number with the highest precision available.
The actual accuracy would depend on the instrumentation and operating system.
"""


def create_container_uptime(
    meter: Meter, callbacks: Optional[Sequence[CallbackT]]
) -> ObservableGauge:
    """The time the container has been running"""
    return meter.create_observable_gauge(
        name=CONTAINER_UPTIME,
        callbacks=callbacks,
        description="The time the container has been running.",
        unit="s",
    )
